﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using PSE.Framework.UI.Exceptions;

namespace PSE.Framework.UI.Base.Model
{
    /// <summary>
    /// Indica o tipo de alteração que o item sofreu
    /// </summary>
    public enum State
    {
        /// <summary>
        /// Item não foi alterado
        /// </summary>
        Unchanged = 0,

        /// <summary>
        /// Item incluído
        /// </summary>
        Inserted = 1,

        /// <summary>
        /// Item alterado
        /// </summary>
        Updated = 2,

        /// <summary>
        /// Item excluído
        /// </summary>
        Deleted = 3
    }

    /// <summary>
    /// Classe base para models
    /// </summary>
    [Serializable]
    public abstract class BaseModel : ObservableObject
    {
        public BaseModel()
        {
            _errors = new ObservableCollection<ErrorDetail>();
            _readOnlyErrors = new ReadOnlyObservableCollection<ErrorDetail>(_errors);
        }

        private State _state = State.Unchanged;
        /// <summary>
        /// Indica o tipo de alteração que a model sofreu
        /// </summary>
        public State State
        {
            get { return _state; }
            internal set
            {
                _state = value;
                this.OnPropertyChanged("State");

                // Limpa os erros
                if (_state == State.Unchanged)
                    this.ClearErrors();
            }
        }

        private bool _hasErrors = false;

        public bool HasErrors
        {
            get { return _hasErrors; }
            private set { _hasErrors = value; this.OnPropertyChanged("HasErrors"); }
        }

        private ObservableCollection<ErrorDetail> _errors = null;
        private ReadOnlyObservableCollection<ErrorDetail> _readOnlyErrors = null;

        public ReadOnlyObservableCollection<ErrorDetail> Errors
        {
            get { return _readOnlyErrors; }
        }

        /// <summary>
        /// Adicionar um erro, baseado na exception informada, na coleção de erros do objeto
        /// </summary>
        /// <param name="ex"></param>
        public void AddError(Exception ex)
        {
            if (ex == null)
                throw new ArgumentNullException("ex");

            // Se não for uma das exceptions abaixo, repassa o erro para a presenter tratar
            if (!(ex is BusinessException) &&
                !(ex is ValidationException))
            {
                throw ex;
            }

            foreach (ErrorDetail detail in ErrorDetail.CreateFromException(ex))
            {
                _errors.Add(detail);
            }

            this.HasErrors = true;
        }

        /// <summary>
        /// Adicionar um erro, baseado na mensagem informada, na coleção de erros do objeto
        /// </summary>
        /// <param name="message"></param>
        public void AddError(string message)
        {
            if (string.IsNullOrEmpty(message))
                throw new ArgumentNullException("message");

            //TODO:Verificar se o id gerado dessa forma está ok
            ErrorDetail error = new ErrorDetail(_errors.Count + 1, message);
            _errors.Add(error);

            this.HasErrors = true;
        }

        public void ClearErrors()
        {
            _errors.Clear();
            this.HasErrors = false;
        }

    }
}
